
#ifdef WX_PRECOMP
#include "wx_pch.h"
#else
#include <wx/msgdlg.h>
#endif

#include <wx/file.h>
#include <wx/filename.h>
#include <wx/dataobj.h>
#include <wx/dnd.h>
#include <wx/filename.h>
#include "wx/mimetype.h"


#include "sdk.h"
#ifndef CB_PRECOMP
#include "manager.h"
#include "editormanager.h"
#include "logmanager.h"
#include "globals.h"
#include "infowindow.h"
#endif


#include "cbstyledtextctrl.h"
#include "snippetitemdata.h"
#include "codesnippetstreectrl.h"
#include "codesnippetswindow.h"
#include "snippetsconfig.h"
#include "GenericMessageBox.h"
#include "menuidentifiers.h"
#include "snippetsconfig.h"
#include "FileImport.h"
#include "version.h"

#include <wx/arrimpl.cpp>
WX_DEFINE_OBJARRAY(EditorSnippetIdArray);

IMPLEMENT_DYNAMIC_CLASS(CodeSnippetsTreeCtrl, wxTreeCtrl)

BEGIN_EVENT_TABLE(CodeSnippetsTreeCtrl, wxTreeCtrl)
  EVT_TREE_BEGIN_DRAG(idSnippetsTreeCtrl, CodeSnippetsTreeCtrl::OnBeginTreeItemDrag)
  EVT_TREE_END_DRAG(idSnippetsTreeCtrl,   CodeSnippetsTreeCtrl::OnEndTreeItemDrag)

  EVT_LEAVE_WINDOW(CodeSnippetsTreeCtrl::OnLeaveWindow)
  EVT_ENTER_WINDOW(CodeSnippetsTreeCtrl::OnEnterWindow)





  EVT_TREE_SEL_CHANGED(idSnippetsTreeCtrl, CodeSnippetsTreeCtrl::OnItemSelected)
  EVT_TREE_ITEM_RIGHT_CLICK(idSnippetsTreeCtrl, CodeSnippetsTreeCtrl::OnItemRightSelected)




END_EVENT_TABLE()


CodeSnippetsTreeCtrl::CodeSnippetsTreeCtrl(wxWindow *parent, const wxWindowID id,
    const wxPoint& pos, const wxSize& size, long style)

  : wxTreeCtrl(parent, id, pos, size, style, wxDefaultValidator, wxT("csTreeCtrl")) {
  m_fileChanged = false;
  m_bMouseExitedWindow = false;
  m_pPropertiesDialog = 0;
  m_bShutDown = false;
  m_mimeDatabase = 0;
  m_bBeginInternalDrag = false;
  m_LastXmlModifiedTime = time_t(0);
  #if !wxCHECK_VERSION(2, 8, 12)
  m_itemAtKeyUp = m_itemAtKeyDown = 0;
  #endif
  m_pSnippetsTreeCtrl = this;
  GetConfig()->SetSnippetsTreeCtrl(this);
  m_pDragCursor = new wxCursor(wxCURSOR_HAND);
  m_bDragCursorOn = false;
  m_oldCursor = GetCursor();
  Manager::Get()->RegisterEventSink(cbEVT_EDITOR_SAVE, new cbEventFunctor<CodeSnippetsTreeCtrl, CodeBlocksEvent>(this, &CodeSnippetsTreeCtrl::OnEditorSave));
  Manager::Get()->RegisterEventSink(cbEVT_EDITOR_CLOSE, new cbEventFunctor<CodeSnippetsTreeCtrl, CodeBlocksEvent>(this, &CodeSnippetsTreeCtrl::OnEditorClose));
}

CodeSnippetsTreeCtrl::~CodeSnippetsTreeCtrl() {
  GetConfig()->SetSnippetsTreeCtrl(0);
}

bool CodeSnippetsTreeCtrl::IsFileSnippet(wxTreeItemId treeItemId) {
  wxTreeItemId itemId = treeItemId;
  if(itemId == (void*)0)
    itemId = GetSelection();
  if(not itemId.IsOk())
    return false;
  if(not IsSnippet(itemId))
    return false;
  wxString fileName = GetSnippetString(itemId).BeforeFirst('\r');
  fileName = fileName.BeforeFirst('\n');
  static const wxString delim(_T("$%["));
  if(fileName.find_first_of(delim) != wxString::npos)
    Manager::Get()->GetMacrosManager()->ReplaceMacros(fileName);
  if(not ::wxFileExists(fileName))
    return false;
  return true;
}

bool CodeSnippetsTreeCtrl::IsFileLinkSnippet(wxTreeItemId treeItemId) {
  wxTreeItemId itemId = treeItemId;
  if(itemId == (void*)0)
    itemId = GetSelection();
  if(not itemId.IsOk())
    return false;
  if(not IsSnippet(itemId))
    return false;
  wxString fileName = GetSnippetString(itemId).BeforeFirst('\r');
  fileName = fileName.BeforeFirst('\n');
  static const wxString delim(_T("$%["));
  if(fileName.find_first_of(delim) != wxString::npos)
    Manager::Get()->GetMacrosManager()->ReplaceMacros(fileName);
  if(fileName.Length() > 128)
    return false;
  if(not ::wxFileExists(fileName))
    return false;
  return true;
}
wxString CodeSnippetsTreeCtrl::GetFileLinkExt(wxTreeItemId treeItemId) {
  if(not IsFileLinkSnippet(treeItemId))
    return wxT("");
  wxTreeItemId itemId = treeItemId;
  if(itemId == (void*)0)
    itemId = GetSelection();
  if(not itemId.IsOk())
    return wxT("");
  if(not IsSnippet(itemId))
    return wxT("");
  wxString fileName = GetSnippetString(itemId).BeforeFirst('\r');
  fileName = fileName.BeforeFirst('\n');
  static const wxString delim(_T("$%["));
  if(fileName.find_first_of(delim) != wxString::npos)
    Manager::Get()->GetMacrosManager()->ReplaceMacros(fileName);
  if(not ::wxFileExists(fileName))
    return wxT("");
  wxFileName filename(fileName);
  return filename.GetExt();
}

void CodeSnippetsTreeCtrl::OnItemSelectChanging(wxTreeEvent& event) {
  return;
}

void CodeSnippetsTreeCtrl::OnItemSelected(wxTreeEvent& event) {
  if(const SnippetTreeItemData* eventItem =
        (SnippetTreeItemData*)GetItemData(event.GetItem())) {
    wxString itemDescription;
    switch(eventItem->GetType()) {
      case SnippetTreeItemData::TYPE_ROOT:
        break;
      case SnippetTreeItemData::TYPE_CATEGORY:
        break;
      case SnippetTreeItemData::TYPE_SNIPPET:
        break;
    }
    m_MnuAssociatedItemID = eventItem->GetId();
  }
}

void CodeSnippetsTreeCtrl::OnItemRightSelected(wxTreeEvent& event) {
  SelectItem(event.GetItem());
}

int CodeSnippetsTreeCtrl::OnCompareItems(const wxTreeItemId& item1, const wxTreeItemId& item2) {
  const SnippetTreeItemData* data1 = (SnippetTreeItemData*)(GetItemData(item1));
  const SnippetTreeItemData* data2 = (SnippetTreeItemData*)(GetItemData(item2));
  if(data1 && data2) {
    int compareIndex1 = 0;
    switch(data1->GetType()) {
      case SnippetTreeItemData::TYPE_ROOT:
        compareIndex1 = 0;
        break;
      case SnippetTreeItemData::TYPE_CATEGORY:
        compareIndex1 = 1;
        break;
      case SnippetTreeItemData::TYPE_SNIPPET:
        compareIndex1 = 2;
        break;
    }
    int compareIndex2 = 0;
    switch(data2->GetType()) {
      case SnippetTreeItemData::TYPE_ROOT:
        compareIndex2 = 0;
        break;
      case SnippetTreeItemData::TYPE_CATEGORY:
        compareIndex2 = 1;
        break;
      case SnippetTreeItemData::TYPE_SNIPPET:
        compareIndex2 = 2;
        break;
    }
    if(compareIndex1 == compareIndex2)
      return wxTreeCtrl::OnCompareItems(item1, item2);
    else if(compareIndex1 > compareIndex2)
      return 1;
    else
      return -1;
  } else
    return 0;
}

wxTreeItemId CodeSnippetsTreeCtrl::FindTreeItemByLabel(const wxString& searchTerms, const wxTreeItemId& node, int requestType) {
  wxTreeItemIdValue cookie;
  wxTreeItemId item = GetFirstChild(node, cookie);
  while(item.IsOk()) {
    if(const SnippetTreeItemData* itemData = (SnippetTreeItemData*)(GetItemData(item))) {
      bool ignoreThisType = false;
      switch(itemData->GetType()) {
        case SnippetTreeItemData::TYPE_ROOT:
          ignoreThisType = true;
          break;
        case SnippetTreeItemData::TYPE_SNIPPET:
          if(requestType == CodeSnippetsConfig::SCOPE_CATEGORIES)
            ignoreThisType = true;
          break;
        case SnippetTreeItemData::TYPE_CATEGORY:
          if(requestType == CodeSnippetsConfig::SCOPE_SNIPPETS)
            ignoreThisType = true;
          break;
      }
      if(!ignoreThisType) {
        wxString label = GetItemText(item);
        if(0 == label.Cmp(searchTerms))
          return item;
      }
      if(ItemHasChildren(item)) {
        wxTreeItemId search = FindTreeItemByLabel(searchTerms, item, requestType);
        if(search.IsOk())
          return search;
      }
      item = GetNextChild(node, cookie);
    }
  }
  wxTreeItemId dummyItem = (void*)(0);
  return dummyItem;
}

wxTreeItemId CodeSnippetsTreeCtrl::FindTreeItemByTreeId(const wxTreeItemId& itemToFind, const wxTreeItemId& startNode, int itemToFindType) {
  wxTreeItemIdValue cookie;
  wxTreeItemId item = GetFirstChild(startNode, cookie);
  while(item.IsOk()) {
    if(const SnippetTreeItemData* itemData = (SnippetTreeItemData*)(GetItemData(item))) {
      bool ignoreThisType = false;
      switch(itemData->GetType()) {
        case SnippetTreeItemData::TYPE_ROOT:
          ignoreThisType = true;
          break;
        case SnippetTreeItemData::TYPE_SNIPPET:
          if(itemToFindType == CodeSnippetsConfig::SCOPE_CATEGORIES)
            ignoreThisType = true;
          break;
        case SnippetTreeItemData::TYPE_CATEGORY:
          if(itemToFindType == CodeSnippetsConfig::SCOPE_SNIPPETS)
            ignoreThisType = true;
          break;
      }
      if(!ignoreThisType) {
        wxString label = GetItemText(item);
        if(itemToFind == item)
          return item;
      }
      if(ItemHasChildren(item)) {
        wxTreeItemId search = FindTreeItemByTreeId(itemToFind, item, itemToFindType);
        if(search.IsOk())
          return search;
      }
      item = GetNextChild(startNode, cookie);
    }
  }
  wxTreeItemId dummyItem = (void*)(0);
  return dummyItem;
}

wxTreeItemId CodeSnippetsTreeCtrl::FindTreeItemBySnippetId(const SnippetItemID& IDToFind, const wxTreeItemId& startNode) {
  static wxTreeItemId dummyItem = (void*)(0);
  wxTreeItemIdValue cookie;
  wxTreeItemId item = GetFirstChild(startNode, cookie);
  while(item.IsOk()) {
    if(const SnippetTreeItemData* itemData = (SnippetTreeItemData*)(GetItemData(item))) {
      bool ignoreThisItem = false;
      #if defined(LOGGING)
      #endif
      switch(itemData->GetType()) {
        case SnippetTreeItemData::TYPE_ROOT:
          ignoreThisItem = true;
          break;
        case SnippetTreeItemData::TYPE_SNIPPET:
          if(IDToFind not_eq itemData->GetID())
            ignoreThisItem = true;
          break;
        case SnippetTreeItemData::TYPE_CATEGORY:
          if(IDToFind not_eq itemData->GetID())
            ignoreThisItem = true;
          break;
      }
      if(!ignoreThisItem) {
        wxString label = GetItemText(item);
        if(IDToFind == itemData->GetID())
          return item;
      }
      if(ItemHasChildren(item)) {
        wxTreeItemId search = FindTreeItemBySnippetId(IDToFind, item);
        if(search.IsOk())
          return search;
      }
      item = GetNextChild(startNode, cookie);
    }
  }
  return dummyItem;
}

void CodeSnippetsTreeCtrl::SaveItemsToXmlNode(TiXmlNode* node, const wxTreeItemId& parentID) {
  wxTreeItemIdValue cookie;
  wxTreeItemId item = GetFirstChild(parentID, cookie);
  while(item.IsOk()) {
    if(const SnippetTreeItemData* data = (SnippetTreeItemData*)(GetItemData(item))) {
      TiXmlElement element("item");
      element.SetAttribute("name", csU2C(GetItemText(item)));
      if(data->GetType() == SnippetTreeItemData::TYPE_CATEGORY) {
        element.SetAttribute("type", "category");
        element.SetAttribute("ID", csU2C(data->GetSnippetIDStr()));
        if(ItemHasChildren(item))
          SaveItemsToXmlNode(&element, item);
      } else {
        element.SetAttribute("type", "snippet");
        element.SetAttribute("ID", csU2C(data->GetSnippetIDStr()));
        TiXmlElement snippetElement("snippet");
        TiXmlText snippetElementText(csU2C(data->GetSnippetString()));
        snippetElement.InsertEndChild(snippetElementText);
        element.InsertEndChild(snippetElement);
      }
      node->InsertEndChild(element);
      item = GetNextChild(parentID, cookie);
    } else
      return;
  }
}

void CodeSnippetsTreeCtrl::LoadItemsFromXmlNode(const TiXmlElement* node, const wxTreeItemId& parentID) {
  for(; node; node = node->NextSiblingElement()) {
    const wxString itemName(csC2U(node->Attribute("name")));
    const wxString itemType(csC2U(node->Attribute("type")));
    const wxString itemIDstr(csC2U(node->Attribute("ID")));
    long itemID;
    itemIDstr.ToLong(&itemID);
    if(itemType == _T("category")) {
      wxTreeItemId newCategoryId = AddCategory(parentID, itemName, itemID, false);
      if(!node->NoChildren())
        LoadItemsFromXmlNode(node->FirstChildElement(), newCategoryId);
    } else if(itemType == _T("snippet")) {
      if(const TiXmlElement* snippetElement = node->FirstChildElement("snippet")) {
        if(const TiXmlNode* snippetElementText = snippetElement->FirstChild()) {
          if(snippetElementText->ToText())
            AddCodeSnippet(parentID, itemName, csC2U(snippetElementText->Value()), itemID,  false);
        } else
          AddCodeSnippet(parentID, itemName, wxEmptyString, itemID, false);
      } else
        GenericMessageBox(_T("CodeSnippets: Error loading XML file; element \"snippet\" cannot be found."));
    } else {
      GenericMessageBox(_T("CodeSnippets: Error loading XML file; attribute \"type\" is \"") + itemType + _T("\" which is invalid item type."));
      return;
    }
  }
}

void CodeSnippetsTreeCtrl::SaveItemsToFile(const wxString& fileName) {
  CreateDirLevels(fileName);
  SnippetTreeItemData::SetHighestSnippetID(0);
  ResetSnippetsIDs(GetRootItem());
  TiXmlDocument doc;
  TiXmlDeclaration header("1.0", "UTF-8", "yes");
  doc.InsertEndChild(header);
  TiXmlComment comment;
  comment.SetValue("Trust me. I know what I'm doing.");
  doc.InsertEndChild(comment);
  TiXmlElement snippetsElement("snippets");
  SaveItemsToXmlNode(&snippetsElement, GetRootItem());
  doc.InsertEndChild(snippetsElement);
  int rc = doc.SaveFile(fileName.mb_str());
  if(not rc) {
    wxString msg = wxString::Format(_T("ERROR: Failed to save %s"), fileName.c_str());
    wxMessageBox(msg, _T("ERROR"));
  }
  SetFileChanged(false);
  SnippetTreeItemData::SetSnippetsItemsChangedCount(0);
  FetchFileModificationTime();
  #ifdef LOGGING
  if(rc)
    LOGIT(_T("File saved:[%s]"), fileName.c_str());
  #endif
}


bool CodeSnippetsTreeCtrl::LoadItemsFromFile(const wxString& fileName, bool bAppendItems) {
  if(!bAppendItems) {
    DeleteChildren(GetRootItem());
    SnippetTreeItemData::SetHighestSnippetID(0);
    SnippetTreeItemData::SetSnippetsItemsChangedCount(0);
  }
  bool retcode = true;
  if(wxFileExists(fileName)) {
    TiXmlBase::SetCondenseWhiteSpace(false);
    TiXmlDocument doc;
    if(doc.LoadFile(fileName.mb_str())) {
      TiXmlElement* root = doc.RootElement();
      if(root) {
        TiXmlElement* firstChild = root->FirstChildElement("item");
        if(firstChild)
          LoadItemsFromXmlNode(firstChild, GetRootItem());
      }
    } else {
      retcode = false;
      wxString backupFile = fileName;
      backupFile.Append(_T(".bak"));
      wxCopyFile(fileName, backupFile, true);
      if(GetConfig()->IsPlugin()) {
        Manager::Get()->GetLogManager()->DebugLog(_T("CodeSnippets: Cannot load file \"") + fileName + _T("\": ") + csC2U(doc.ErrorDesc()));
        Manager::Get()->GetLogManager()->DebugLog(_T("CodeSnippets: Backup of the failed file has been created."));
      } else {
        GenericMessageBox(_T("CodeSnippets: Cannot load file \"") + fileName + _T("\": ") + csC2U(doc.ErrorDesc()));
        GenericMessageBox(_T("CodeSnippets: Backup of the failed file has been created."));
      }
    }
  }
  if(GetRootItem() && GetRootItem().IsOk())
    Expand(GetRootItem());
  wxString nameOnly;
  wxFileName::SplitPath(fileName, 0, &nameOnly, 0);
  SetItemText(GetRootItem(), wxString::Format(_("%s"), nameOnly.GetData()));
  if(not SnippetTreeItemData::GetSnippetsItemsChangedCount())
    SetFileChanged(false);
  FetchFileModificationTime();
  return retcode;
}

void CodeSnippetsTreeCtrl::AddCodeSnippet(const wxTreeItemId& parent,
    wxString title, wxString codeSnippet, long ID, bool editNow) {
  SnippetTreeItemData* pSnippetDataItem = new SnippetTreeItemData(SnippetTreeItemData::TYPE_SNIPPET, codeSnippet, ID);
  wxTreeItemId newItemID = InsertItem(parent, GetLastChild(parent), title, 2, -1,
                                      pSnippetDataItem);
  SortChildren(parent);
  bool ok = false;
  if(newItemID && newItemID.IsOk())
    ok = true;
  if(editNow) {
    EnsureVisible(newItemID);
    ok = EditSnippetProperties(newItemID);
    if(not ok)
      RemoveItem(newItemID);
    else
      SelectItem(newItemID);
  }
  if(ok && newItemID.IsOk()) {
    SetSnippetImage(newItemID);
    SetFileChanged(true);
  }
}


wxTreeItemId CodeSnippetsTreeCtrl::AddCategory(const wxTreeItemId& parent, wxString title, long ID, bool editNow) {
  wxTreeItemId newCategoryID = InsertItem(parent, GetLastChild(parent), title, 1, -1,
                                          new SnippetTreeItemData(SnippetTreeItemData::TYPE_CATEGORY, ID));
  SortChildren(parent);
  if(editNow) {
    EnsureVisible(newCategoryID);
    EditLabel(newCategoryID);
    SetFileChanged(true);
  }
  return newCategoryID;
}

bool CodeSnippetsTreeCtrl::RemoveItem(const wxTreeItemId RemoveItemId) {
  wxTreeItemId itemId = RemoveItemId;
  if(not itemId.IsOk())
    return false;
  if(itemId == GetRootItem())
    return false;
  SnippetTreeItemData* pItemData = (SnippetTreeItemData*)(GetItemData(itemId));
  if(not pItemData)
    return false;
  bool shiftKeyIsDown = ::wxGetKeyState(WXK_SHIFT);
  wxString itemText = GetItemText(RemoveItemId);
  bool canceledItem = false;
  if(itemText.IsSameAs(wxT("New category")) || itemText.IsSameAs(wxT("New snippet"))) {
    canceledItem = true;
    shiftKeyIsDown = true;
  }
  bool trashItem = false;
  if(not shiftKeyIsDown) {
    wxTreeItemId trashId = FindTreeItemByLabel(wxT(".trash"), GetRootItem(), CodeSnippetsConfig::SCOPE_CATEGORIES);
    if(trashId == (void*)0)
      trashId = AddCategory(GetRootItem(), wxT(".trash"), 0, false);
    if(not(FindTreeItemByTreeId(itemId, trashId, pItemData->GetType()))) {
      TiXmlDocument* pDoc =  CopyTreeNodeToXmlDoc(itemId);
      CopyXmlDocToTreeNode(pDoc, trashId);
      delete pDoc;
    } else
      trashItem = true;
  }
  if(not canceledItem)
    if(trashItem || shiftKeyIsDown) {
      wxString filename = wxEmptyString;
      if(IsFileSnippet(itemId))
        filename = GetSnippetFileLink(itemId);
      if(not filename.IsEmpty()) {
        int answer = GenericMessageBox(wxT("Delete physical file?\n\n") + filename,
                                       wxT("Delete"), wxYES_NO);
        if(answer == wxYES)
          ::wxRemoveFile(filename);
      }
    }
  DeleteChildren(itemId);
  Delete(itemId);
  SetFileChanged(true);
  return true;
}

void CodeSnippetsTreeCtrl::SetSnippetImage(wxTreeItemId itemId) {
  if(IsFileSnippet(itemId))
    SetItemImage(itemId, TREE_IMAGE_SNIPPET_FILE);
  else if(IsUrlSnippet(itemId))
    SetItemImage(itemId, TREE_IMAGE_SNIPPET_URL);
  else
    SetItemImage(itemId, TREE_IMAGE_SNIPPET_TEXT);
  return;
}

void CodeSnippetsTreeCtrl::FetchFileModificationTime(wxDateTime savedTime) {
  if(savedTime != time_t(0)) {
    m_LastXmlModifiedTime = savedTime;
    return;
  }
  if(::wxFileExists(GetConfig()->SettingsSnippetsXmlPath)) {
    wxFileName fname(GetConfig()->SettingsSnippetsXmlPath);
    m_LastXmlModifiedTime = fname.GetModificationTime();
  }
}

void CodeSnippetsTreeCtrl::OnBeginTreeItemDrag(wxTreeEvent& event) {
  CodeSnippetsTreeCtrl* pTree = this;
  #ifdef LOGGING
  LOGIT(_T("TREE_BEGIN_DRAG %p"), pTree);
  #endif
  m_bBeginInternalDrag = true;
  m_TreeItemId = event.GetItem();
  m_itemAtKeyDown = m_TreeItemId;
  m_MnuAssociatedItemID = m_TreeItemId;
  m_TreeMousePosn = event.GetPoint();
  m_TreeText = pTree->GetSnippetString(m_TreeItemId);
  if(IsCategory(m_TreeItemId))
    m_TreeText = GetSnippetLabel(m_TreeItemId);
  if(m_TreeText.IsEmpty())
    m_bBeginInternalDrag = 0;
  event.Allow();
  return;
}

void CodeSnippetsTreeCtrl::EndInternalTreeItemDrag() {
  if(not m_itemAtKeyUp)
    return;
  #ifdef LOGGING
  wxTreeCtrl* pTree = this;
  LOGIT(_T("TREE_END_DRAG %p"), pTree);
  #endif
  wxTreeItemId targetItem = m_itemAtKeyUp;
  wxTreeItemId sourceItem = m_itemAtKeyDown;
  if(not sourceItem.IsOk())
    return;
  if(not targetItem.IsOk())
    return;
  if(not m_bBeginInternalDrag)
    return;
  if(m_bMouseExitedWindow)
    return;
  if(IsSnippet(targetItem)) {
    if((targetItem = ConvertSnippetToCategory(targetItem)))
      ;
    else
      return;
  }
  TiXmlDocument* pDoc = CopyTreeNodeToXmlDoc(sourceItem);
  if(not pDoc)
    return;
  TiXmlElement* root = pDoc->RootElement();
  if(root) {
    TiXmlElement* firstChild = root->FirstChildElement("item");
    if(firstChild)
      LoadItemsFromXmlNode(firstChild, targetItem);
  }
  if(not m_bMouseCtrlKeyDown)
    RemoveItem(sourceItem);
  delete pDoc;
  pDoc = 0;
  #if !wxCHECK_VERSION(2, 8, 12)
  m_itemAtKeyDown = 0;
  m_itemAtKeyUp = 0;
  #endif
  return;
}

void CodeSnippetsTreeCtrl::OnEnterWindow(wxMouseEvent& event) {
  #if defined(LOGGING)
  #endif
  if(not GetConfig()->GetSnippetsWindow()->IsEditingLabel())
    if(GetConfig()->IsFloatingWindow()) {
      #if defined(LOGGING)
      #endif
      wxWindow* pw = (wxWindow*)event.GetEventObject();
      if(pw == ::wxGetActiveWindow()) {
        pw->Enable();
        pw->SetFocus();
      }
    }
  event.Skip();
}

void CodeSnippetsTreeCtrl::OnLeaveWindow(wxMouseEvent& event) {
  #ifdef LOGGING
  #endif
  m_bBeginInternalDrag = false;
  if(m_TreeText.IsEmpty())
    return;
  #ifdef LOGGING
  LOGIT(_T("TreeCtrl LEAVE_WINDOW %p"), event.GetEventObject());
  #endif
  m_bMouseExitedWindow = true;
  #if defined(__WXMSW__)
  if(m_bMouseExitedWindow
      and (not  m_TreeText.IsEmpty())) {
    FinishExternalDrag();
    SendMouseLeftUp(this, m_TreeMousePosn.x, m_TreeMousePosn.y);
  }
  #endif
  return;
}

void CodeSnippetsTreeCtrl::FinishExternalDrag() {
  m_bMouseExitedWindow = false;
  if(m_TreeText.IsEmpty())
    return;
  wxTextDataObject* textData = new wxTextDataObject();
  wxFileDataObject* fileData = new wxFileDataObject();
  wxString textStr = GetSnippetString(m_itemAtKeyDown) ;
  static const wxString delim(_T("$%["));
  if(textStr.find_first_of(delim) != wxString::npos)
    Manager::Get()->GetMacrosManager()->ReplaceMacros(textStr);
  wxDropSource textSource(*textData, this);
  textData->SetText(textStr);
  wxDropSource fileSource(*fileData, this);
  wxString fileName = GetSnippetFileLink(m_itemAtKeyDown);
  if(not ::wxFileExists(fileName))
    fileName = wxEmptyString;
  if(fileName.IsEmpty()) {
    if(textStr.StartsWith(_T("http://")))
      fileName = textStr;
    if(textStr.StartsWith(_T("file://")))
      fileName = textStr;
    fileName = fileName.BeforeFirst('\n');
    fileName = fileName.BeforeFirst('\r');
    if(not fileName.IsEmpty())
      textData->SetText(fileName);
  }
  fileData->AddFile((fileName.Len() > 128) ? wxString(wxEmptyString) : fileName);
  wxDataObjectComposite *data = new wxDataObjectComposite();
  data->Add((wxDataObjectSimple*)textData);
  data->Add((wxDataObjectSimple*)fileData, true);
  wxDropSource source(*data, this);
  #ifdef LOGGING
  LOGIT(_T("DropSource Text[%s], File[%s]"),
        textData->GetText().c_str(),
        fileData->GetFilenames().Item(0).c_str());
  #endif
  int flags = 0;
  flags |= wxDrag_AllowMove;
  wxDragResult result = source.DoDragDrop(flags);
  #if LOGGING
  wxString pc;
  switch(result) {
    case wxDragError:
      pc = _T("Error!");
      break;
    case wxDragNone:
      pc = _T("Nothing");
      break;
    case wxDragCopy:
      pc = _T("Copied");
      break;
    case wxDragMove:
      pc = _T("Moved");
      break;
    case wxDragLink:
      pc = _T("Linked");
      break;
    case wxDragCancel:
      pc = _T("Cancelled");
      break;
    default:
      pc = _T("Huh?");
      break;
  }
  LOGIT(wxT("DoDragDrop returned[%s]"), pc.GetData());
  #else
  wxUnusedVar(result);
  #endif
  delete textData;
  delete fileData;
  m_TreeText = wxEmptyString;
  #if !wxCHECK_VERSION(2, 8, 12)
  m_itemAtKeyDown = 0;
  m_itemAtKeyUp = 0;
  #endif
}

void CodeSnippetsTreeCtrl::OnEndTreeItemDrag(wxTreeEvent& event) {
  m_MouseUpX = event.GetPoint().x;
  m_MouseUpY = event.GetPoint().y;
  #if !wxCHECK_VERSION(2, 8, 12)
  m_itemAtKeyUp = 0;
  #endif
  int hitFlags = 0;
  wxTreeItemId id = HitTest(wxPoint(m_MouseUpX, m_MouseUpY), hitFlags);
  if(id.IsOk() and (hitFlags & (wxTREE_HITTEST_ONITEMICON | wxTREE_HITTEST_ONITEMLABEL)))
    m_itemAtKeyUp = id;
  #ifdef LOGGING
  #endif
  if(m_bMouseExitedWindow
      and (not  m_TreeText.IsEmpty())) {
    #if defined(__WXMSW__)
    #endif
  } else if((not m_bMouseExitedWindow)
            and m_itemAtKeyDown and m_itemAtKeyUp
            and (m_itemAtKeyDown not_eq m_itemAtKeyUp))
    EndInternalTreeItemDrag();
  m_bMouseExitedWindow = false;
  m_bMouseIsDragging = false;
}

void CodeSnippetsTreeCtrl::OnMouseWheelEvent(wxMouseEvent& event) {
  m_bMouseCtrlKeyDown = event.ControlDown();
  #ifdef LOGGING
  #endif
  if(not m_bMouseCtrlKeyDown) {
    event.Skip();
    return;
  }
  int nRotation = event.GetWheelRotation();
  wxFont ctrlFont = GetFont();
  if(nRotation > 0)
    ctrlFont.SetPointSize(ctrlFont.GetPointSize() - 1);
  else
    ctrlFont.SetPointSize(ctrlFont.GetPointSize() + 1);
  SetFont(ctrlFont);
  return;
}

wxTreeItemId CodeSnippetsTreeCtrl::ConvertSnippetToCategory(wxTreeItemId itemId) {
  wxTreeItemId badItemId = (void*)0;
  if(not IsSnippet(itemId))
    return badItemId;
  wxTreeItemId oldItemId = itemId;
  if(not oldItemId.IsOk())
    return badItemId;
  wxTreeItemId itemParent = GetItemParent(oldItemId);
  TiXmlDocument* pDoc = CopyTreeNodeToXmlDoc(oldItemId);
  if(not pDoc)
    return badItemId;
  wxTreeItemId newCategoryId = AddCategory(itemParent,
                               GetItemText(oldItemId), GetSnippetID(oldItemId), false);
  TiXmlElement* root = pDoc->RootElement();
  if(root) {
    TiXmlElement* firstChild = root->FirstChildElement("item");
    if(firstChild)
      LoadItemsFromXmlNode(firstChild, newCategoryId);
  }
  RemoveItem(oldItemId);
  delete pDoc;
  pDoc = 0;
  return newCategoryId;
}

TiXmlDocument* CodeSnippetsTreeCtrl::CopyTreeNodeToXmlDoc(wxTreeItemId TreeItemId) {
  wxTreeItemId itemId = TreeItemId;
  if(not itemId.IsOk())
    itemId = GetSelection();
  if(not itemId.IsOk())
    return NULL;
  TiXmlDocument* pDoc = new TiXmlDocument;
  TiXmlDeclaration header("1.0", "UTF-8", "yes");
  pDoc->InsertEndChild(header);
  TiXmlElement snippetsElement("snippets");
  CopySnippetsToXmlDoc(&snippetsElement, itemId);
  pDoc->InsertEndChild(snippetsElement);
  return pDoc;
}

void CodeSnippetsTreeCtrl::CopySnippetsToXmlDoc(TiXmlNode* Node, const wxTreeItemId& itemID) {
  wxTreeItemIdValue cookie;
  wxTreeItemId item = itemID;
  while(item.IsOk()) {
    const SnippetTreeItemData* data = (SnippetTreeItemData*)GetItemData(item);
    if(!data)
      return;
    TiXmlElement element("item");
    element.SetAttribute("name", GetItemText(item).mb_str());
    switch(data->GetType()) {
      case SnippetTreeItemData::TYPE_CATEGORY:
        element.SetAttribute("type", "category");
        break;
      case SnippetTreeItemData::TYPE_SNIPPET:
        element.SetAttribute("type", "snippet");
        break;
      default:
        break;
    }
    if(data->GetType() == SnippetTreeItemData::TYPE_SNIPPET) {
      TiXmlElement snippetElement("snippet");
      TiXmlText snippetElementText(data->GetSnippetString().mb_str());
      snippetElement.InsertEndChild(snippetElementText);
      element.InsertEndChild(snippetElement);
    }
    if(ItemHasChildren(item))
      SaveItemsToXmlNode(&element, item);
    Node->InsertEndChild(element);
    item = GetNextChild(itemID, cookie);
  }
}

void CodeSnippetsTreeCtrl::CopyXmlDocToTreeNode(TiXmlDocument* pTiXmlDoc, wxTreeItemId targetItem) {
  TiXmlElement* root = pTiXmlDoc->RootElement();
  if(root) {
    TiXmlElement* firstChild = root->FirstChildElement("item");
    if(firstChild)
      LoadItemsFromXmlNode(firstChild, targetItem);
  }
}

void CodeSnippetsTreeCtrl::EditSnippetAsFileLink() {
  #if defined(LOGGING)
  LOGIT(_T("EditSnippetAsFileLink[%s]"), wxT(""));
  #endif
  if(not IsSnippet())
    return;
  wxTreeItemId itemId = GetAssociatedItemID();
  SnippetTreeItemData* pSnippetTreeItemData = (SnippetTreeItemData*)GetItemData(GetAssociatedItemID());
  wxString FileName = GetSnippetFileLink(itemId);
  LOGIT(_T("EditSnippetsAsFileLlink()FileName[%s]"), FileName.c_str());
  if(FileName.Length() > 128) {
    EditSnippetAsText();
    return;
  }
  if((FileName.IsEmpty())
      || (not ::wxFileExists(FileName))) {
    EditSnippetAsText();
    return;
  }
  wxString pgmName = GetConfig()->SettingsExternalEditor;
  #if defined(LOGGING)
  LOGIT(_T("PgmName[%s]"), pgmName.c_str());
  #endif
  if(pgmName.IsEmpty() || (not ::wxFileExists(pgmName))) {
    EditSnippet(pSnippetTreeItemData, FileName);
    return;
  }
  if(::wxFileExists(pgmName)) {
    wxString execString = pgmName + wxT(" \"") + FileName + wxT("\"");
    #ifdef LOGGING
    LOGIT(_T("OpenAsFileLink[%s]"), execString.GetData());
    #endif
    ::wxExecute(execString);
  }
  return;
}

void CodeSnippetsTreeCtrl::OpenSnippetAsFileLink() {
  if(not IsSnippet())
    return;
  wxTreeItemId itemId = GetAssociatedItemID();
  wxString FileName = GetSnippetFileLink(itemId);
  LOGIT(_T("OpenSnippetsAsFileLlink()FileName[%s]"), FileName.c_str());
  if(FileName.Length() > 128) {
    EditSnippetAsText();
    return;
  }
  EditSnippetWithMIME();
  return;
}

void CodeSnippetsTreeCtrl::EditSnippetAsText() {
  #if defined(LOGGING)
  LOGIT(_T("EditSnippetAsText[%s]"), wxT(""));
  #endif
  SnippetTreeItemData* pSnippetTreeItemData = (SnippetTreeItemData*)GetItemData(GetAssociatedItemID());
  wxString editorName = GetConfig()->SettingsExternalEditor ;
  if(editorName.IsEmpty() || (not ::wxFileExists(editorName))) {
    EditSnippet(pSnippetTreeItemData);
    return;
  }
  if(editorName.IsEmpty() || (not ::wxFileExists(editorName))) {
    #if defined(__WXMSW__)
    editorName = wxT("notepad");
    #elif defined(__UNIX__)
    editorName = wxT("vi");
    #endif
    wxString msg(wxT("Using default editor: ") + editorName + wxT("\n"));
    if(GetConfig()->IsApplication())
      msg = msg + wxT("Use Menu->");
    else
      msg = msg + wxT("Right click Root item. Use ");
    msg = msg + wxT("Settings to set a better editor.\n");
    GenericMessageBox(msg);
  }
  if(IsFileSnippet(GetAssociatedItemID())) {
    wxString filename = wxEmptyString;
    filename = GetSnippetFileLink(GetAssociatedItemID());
    wxString execString = editorName + wxT(" \"") + filename + wxT("\"");
    if(::wxFileExists(filename))
      ::wxExecute(execString, wxEXEC_ASYNC);
    else
      cbMessageBox(_T("File does not Exist\n") + filename, _T("Error"));
  } else
    EditSnippet(pSnippetTreeItemData);
  return;
}

void CodeSnippetsTreeCtrl::SaveSnippetAsFileLink() {
  wxTreeItemId itemId = GetAssociatedItemID();
  if(not itemId.IsOk())
    return;
  if(not IsSnippet())
    return;
  wxString snippetLabel = GetSnippetLabel();
  wxString snippetData = GetSnippetString();
  wxString fileName = GetSnippetFileLink();
  int answer = wxYES;
  if(::wxFileExists(fileName)) {
    answer = GenericMessageBox(
               wxT("Item is already a file link named:\n") + fileName
               + wxT(" \n\nAre you sure you want to rewrite the file?\n"),
               wxT("Warning"), wxYES | wxNO);
    if(wxYES == answer) {
      wxFile oldFile(fileName, wxFile::read);
      if(not oldFile.IsOpened()) {
        GenericMessageBox(wxT("Abort.Error reading data file."));
        return;
      }
      unsigned long fileSize = oldFile.Length();
      char* pBuf = new char[fileSize + 1];
      oldFile.Read(pBuf, fileSize);
      pBuf[fileSize] = 0;
      snippetData = csC2U(pBuf);
      oldFile.Close();
      delete [] pBuf;
    }
  }
  if(wxNO == answer)
    return;
  wxString newFileName = snippetLabel + wxT(".txt");
  wxFileName snippetFileName(newFileName) ;
  static const wxString delim(_T("$%["));
  if(newFileName.find_first_of(delim) != wxString::npos)
    Manager::Get()->GetMacrosManager()->ReplaceMacros(newFileName);
  wxString forbidden = snippetFileName.GetForbiddenChars();
  for(size_t i = 0; i < forbidden.Length(); ++i)
    newFileName.Replace(wxString(forbidden[i]), wxT(""), true);
  wxFileDialog dlg(this,
                   _("Save as text file"),
                   GetConfig()->SettingsSnippetsFolder,
                   newFileName,
                   wxT("*.*"),
                   wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
  wxPoint mousePosn = ::wxGetMousePosition();
  (&dlg)->Move(mousePosn.x, mousePosn.y);
  if(dlg.ShowModal() != wxID_OK)
    return;
  newFileName = dlg.GetPath();
  #ifdef LOGGING
  LOGIT(_T("New filename[%s]"), newFileName.GetData());
  #endif
  if(newFileName.IsEmpty())
    return;
  wxFile newFile(newFileName, wxFile::write);
  if(not newFile.IsOpened()) {
    GenericMessageBox(wxT("Open failed for:") + newFileName);
    return ;
  }
  newFile.Write(csU2C(snippetData), snippetData.Length());
  newFile.Close();
  SetSnippetString(newFileName);
  if(IsFileSnippet())
    SetItemImage(itemId, TREE_IMAGE_SNIPPET_FILE);
  else
    SetItemImage(itemId, TREE_IMAGE_SNIPPET_TEXT);
  return;
}
bool CodeSnippetsTreeCtrl::EditSnippetProperties(wxTreeItemId& itemId) {
  if(not IsSnippet(itemId))
    return false;
  int result = 0;
  wxSemaphore waitSem;
  SnippetProperty* pdlg = new SnippetProperty(GetSnippetsTreeCtrl(), itemId, &waitSem);
  result = ExecuteDialog(pdlg, waitSem);
  if(result == wxID_OK)
    SetSnippetImage(itemId);
  if(result == wxID_OK)
    SetFileChanged(true);
  pdlg->Destroy();
  return (result = (result == wxID_OK));
}

void CodeSnippetsTreeCtrl::EditSnippet(SnippetTreeItemData* pSnippetTreeItemData, wxString fileName) {
  if((SnippetTreeItemData*)(GetItemData(GetAssociatedItemID()))) {
    wxTreeItemId m_SnippetItemId = GetAssociatedItemID();
    wxString m_EditSnippetText = GetConfig()->GetSnippetsTreeCtrl()->GetSnippetString(m_SnippetItemId);
    wxString m_EditFileName = m_EditSnippetText.BeforeFirst('\r');
    m_EditFileName = m_EditFileName.BeforeFirst('\n');
    static const wxString delim(_T("$%["));
    if(m_EditFileName.find_first_of(delim) != wxString::npos)
      Manager::Get()->GetMacrosManager()->ReplaceMacros(m_EditFileName);
    if((m_EditFileName.Length() < 129) && (::wxFileExists(m_EditFileName)))
      ;
    else
      m_EditFileName = wxEmptyString;
    wxString m_EditSnippetLabel = GetConfig()->GetSnippetsTreeCtrl()->GetSnippetLabel(m_SnippetItemId);
    if(not m_EditFileName.IsEmpty()) {
      cbEditor* m_pcbEditor = Manager::Get()->GetEditorManager()->Open(m_EditFileName);
      m_EditorPtrArray.Add(m_pcbEditor);
      m_EditorSnippetIdArray.Add(m_SnippetItemId);
    } else {
      wxString m_TmpFileName = wxFileName::GetTempDir();
      m_TmpFileName << wxFILE_SEP_PATH << m_EditSnippetLabel << _T(".txt");
      cbEditor* m_pcbEditor = Manager::Get()->GetEditorManager()->New(m_TmpFileName);
      if(not m_pcbEditor) {
        InfoWindow::Display(_T("File Error"), wxString::Format(_T("File Error: %s"), m_TmpFileName.c_str()), 9000);
        return;
      }
      m_pcbEditor->GetControl()->SetText(m_EditSnippetText);
      m_pcbEditor->SetModified(false);
      m_pcbEditor->GetControl()->EmptyUndoBuffer();
      m_EditorPtrArray.Add(m_pcbEditor);
      m_EditorSnippetIdArray.Add(m_SnippetItemId);
    }
  }
}

void CodeSnippetsTreeCtrl::EditSnippetWithMIME() {
  wxTreeItemId itemId = GetAssociatedItemID();
  if(not itemId.IsOk())
    return;
  if(not IsSnippet())
    return;
  wxString snippetLabel = GetSnippetLabel();
  wxString snippetData = GetSnippetString();
  wxString fileName = GetSnippetFileLink();
  LOGIT(_T("EditSnippetWithMime[%s]"), fileName.c_str());
  if(fileName.IsEmpty())
    return;
  wxFileName file(fileName);
  wxString fileExt = file.GetExt();
  if((fileName.StartsWith(wxT("http://")))
      || (fileName.StartsWith(wxT("file://")))
      || (fileName.StartsWith(wxT("ftp://")))
      || (fileExt == wxT("html"))
      || (fileExt == wxT("htm"))
    ) {
    wxLaunchDefaultBrowser(fileName);
    return;
  }
  if(not ::wxFileExists(fileName))
    return;
  wxString fileNameExt;
  #if wxCHECK_VERSION(3, 0, 0)
  wxFileName::SplitPath(fileName, 0, 0, 0, &fileNameExt);
  #else
  ::wxSplitPath(fileName, 0, 0, &fileNameExt);
  #endif
  if(fileNameExt.IsEmpty())
    return;
  wxString s_defaultExt = _T("xyz");
  wxString msg;
  if(!!fileNameExt) {
    s_defaultExt = fileNameExt;
    if(!m_mimeDatabase)
      m_mimeDatabase = wxTheMimeTypesManager;
    wxFileType *filetype = m_mimeDatabase->GetFileTypeFromExtension(fileNameExt);
    if(!filetype)
      msg << _T("Unknown extension '") << fileNameExt << _T("'\n");
    else {
      wxString type, desc, open;
      filetype->GetMimeType(&type);
      filetype->GetDescription(&desc);
      wxString filename = fileName;
      wxFileType::MessageParameters params(filename, type);
      filetype->GetOpenCommand(&open, params);
      #if LOGGING
      msg << _T("MIME information about extension '") << fileNameExt << _T('\n')
          << _T("\tMIME type: ") << (!type ? wxT("unknown")
                                     : type.c_str()) << _T('\n')
          << _T("\tDescription: ") << (!desc ? wxEmptyString : desc.c_str())
          << _T('\n')
          << _T("\tCommand to open: ") << (!open ? wxT("no") : open.c_str())
          << _T('\n');
      #endif
      delete filetype;
      #if wxCHECK_VERSION(3, 0, 0)
      if(!open.IsEmpty())
      #else
      if(open)
      #endif
        ::wxExecute(open, wxEXEC_ASYNC);
    }
  }
  #ifdef LOGGING
  LOGIT(_T("EditSnippetWithMIME()[%s]"), msg.c_str());
  #endif
  return;
}

int CodeSnippetsTreeCtrl::ExecuteDialog(wxScrollingDialog* pdlg, wxSemaphore& waitSem) {
  if(m_pPropertiesDialog)
    return 0;
  m_pPropertiesDialog = pdlg;
  int retcode = 0;
  wxWindow* pw = this;
  if(pw && pw->GetParent())
    pw = pw->GetParent();
  if(pw && pw->GetParent())
    pw = pw->GetParent();
  GetConfig()->GetMainFrame()->Connect(wxEVT_CLOSE_WINDOW,
                                       (wxObjectEventFunction)(wxEventFunction)
                                       (wxCloseEventFunction) &CodeSnippetsTreeCtrl::OnShutdown, NULL, this);
  pw->Connect(wxEVT_CLOSE_WINDOW,
              (wxObjectEventFunction)(wxEventFunction)
              (wxCloseEventFunction) &CodeSnippetsTreeCtrl::OnShutdown, NULL, this);
  if(GetConfig()->IsPlugin())
    GetConfig()->GetMenuBar()->Enable(idViewSnippets, false);
  if(pdlg->Show()) {
    while(wxSEMA_BUSY == waitSem.TryWait()) {
      waitSem.WaitTimeout(20);
      wxYield();
    }
    retcode = pdlg->GetReturnCode();
  }
  GetConfig()->GetMainFrame()->Disconnect(wxEVT_CLOSE_WINDOW,
                                          (wxObjectEventFunction)(wxEventFunction)
                                          (wxCloseEventFunction) &CodeSnippetsTreeCtrl::OnShutdown);
  pw->Disconnect(wxEVT_CLOSE_WINDOW,
                 (wxObjectEventFunction)(wxEventFunction)
                 (wxCloseEventFunction) &CodeSnippetsTreeCtrl::OnShutdown);
  if(GetConfig()->IsPlugin())
    GetConfig()->GetMenuBar()->Enable(idViewSnippets, true);
  m_pPropertiesDialog = 0;
  return retcode;
}

void CodeSnippetsTreeCtrl::SaveEditorsXmlData(cbEditor* pcbEditor) {
  int idx = m_EditorPtrArray.Index(pcbEditor);
  if(wxNOT_FOUND == idx)
    return;
  wxTreeItemId snippetID = m_EditorSnippetIdArray[idx];
  SnippetTreeItemData* pSnippetTreeItemData = (SnippetTreeItemData*)(GetItemData(snippetID));
  pSnippetTreeItemData->SetSnippetString(pcbEditor->GetControl()->GetText());
  SetFileChanged(true);
}

void CodeSnippetsTreeCtrl::SaveAllOpenEditors() {
  size_t knt = m_EditorPtrArray.GetCount();
  if(not knt)
    return;
  for(size_t ii = 0; ii < knt; ++ii) {
    EditorBase* eb = m_EditorPtrArray[ii];
    int idx = m_EditorPtrArray.Index((cbEditor*)eb);
    if(wxNOT_FOUND == idx)
      continue;
    if(eb) {
      if(eb->GetModified()) {
        int rep = cbMessageBox(wxString::Format(_T("Save? %s"), eb->GetName().wx_str()), _T("Save File?"), wxOK | wxCANCEL, this);
        if(wxID_OK == rep)
          eb->Save();
      }
      eb->Close();
    }
  }
}

void CodeSnippetsTreeCtrl::OnEditorSave(CodeBlocksEvent& event) {
  event.Skip();
  EditorBase* eb = event.GetEditor();
  wxString filePath = event.GetString();
  int idx = m_EditorPtrArray.Index((cbEditor*)eb);
  if(wxNOT_FOUND == idx)
    return;
  if(eb)
    SaveEditorsXmlData((cbEditor*)eb);
}

void CodeSnippetsTreeCtrl::OnEditorClose(CodeBlocksEvent& event) {
  event.Skip();
  EditorBase* eb = event.GetEditor();
  if(not eb)
    return;
  wxString filePath = event.GetString();
  int idx = m_EditorPtrArray.Index((cbEditor*)eb);
  if(wxNOT_FOUND == idx)
    return;
  m_EditorSnippetIdArray.Detach(idx);
  m_EditorPtrArray.RemoveAt(idx);
}

void CodeSnippetsTreeCtrl::OnIdle() {
  if(GetConfig()->IsPlugin())
    GetConfig()->GetMenuBar()->Enable(idViewSnippets, true);
  if(GetConfig()->GetSnippetsSearchCtrl()
      && GetConfig()->GetSnippetsSearchCtrl()->GetValue().IsEmpty()) {
    wxString nameOnly;
    wxFileName::SplitPath(GetConfig()->SettingsSnippetsXmlPath, 0, &nameOnly, 0);
    wxString currentValue = GetItemText(GetRootItem());
    if(currentValue != nameOnly)
      SetItemText(GetRootItem(), wxString::Format(_("%s"), nameOnly.GetData()));
  }
  return;
}

void CodeSnippetsTreeCtrl::OnShutdown(wxCloseEvent& event) {
  event.Skip();
  return;
}

wxTreeItemId CodeSnippetsTreeCtrl::ResetSnippetsIDs(const wxTreeItemId& startNode) {
  static wxTreeItemId dummyItem = (void*)(0);
  wxTreeItemIdValue cookie;
  wxTreeItemId item = GetFirstChild(startNode, cookie);
  while(item.IsOk()) {
    if(SnippetTreeItemData* itemData = (SnippetTreeItemData*)(GetItemData(item))) {
      bool ignoreThisType = false;
      switch(itemData->GetType()) {
        case SnippetTreeItemData::TYPE_ROOT:
          ignoreThisType = true;
          break;
        case SnippetTreeItemData::TYPE_SNIPPET:
          break;
        case SnippetTreeItemData::TYPE_CATEGORY:
          break;
      }
      if(!ignoreThisType)
        itemData->SetID(itemData->GetNewID());
      if(ItemHasChildren(item)) {
        wxTreeItemId search = ResetSnippetsIDs(item);
        if(search.IsOk())
          return search;
      }
      item = GetNextChild(startNode, cookie);
    }
  }
  return dummyItem;
}

wxTreeItemId CodeSnippetsTreeCtrl::FillFileLinksMapArray(const wxTreeItemId& startNode, FileLinksMapArray& fileLinksMapArray) {
  static wxTreeItemId dummyItem = (void*)(0);
  wxTreeItemIdValue cookie;
  wxTreeItemId treeItem = GetFirstChild(startNode, cookie);
  while(treeItem.IsOk()) {
    if(SnippetTreeItemData* snippetData = (SnippetTreeItemData*)(GetItemData(treeItem))) {
      bool ignoreThisType = false;
      switch(snippetData->GetType()) {
        case SnippetTreeItemData::TYPE_ROOT:
          ignoreThisType = true;
          break;
        case SnippetTreeItemData::TYPE_SNIPPET:
          break;
        case SnippetTreeItemData::TYPE_CATEGORY:
          ignoreThisType = true;
          break;
      }
      if(!ignoreThisType) {
        wxString fileName = wxEmptyString;
        if(wxEmptyString not_eq(fileName = snippetData->GetSnippetFileLink())) {
          long itemId = snippetData->GetID();
          fileLinksMapArray[fileName] = itemId;
        }
      }
      if(ItemHasChildren(treeItem)) {
        wxTreeItemId search = FillFileLinksMapArray(treeItem, fileLinksMapArray);
        if(search.IsOk())
          return search;
      }
      treeItem = GetNextChild(startNode, cookie);
    }
  }
  return dummyItem;
}

void CodeSnippetsTreeCtrl::CreateDirLevels(const wxString& pathNameIn) {
  FileImportTraverser fit(_T("dummy"), pathNameIn);
  return;
}

void CodeSnippetsTreeCtrl::SendMouseLeftUp(const wxWindow* pWin, const int mouseX, const int mouseY) {
  #if defined(__WXMSW__)
  if(pWin) {
    #ifdef LOGGING
    LOGIT(_T("Sending Mouse LeftKeyUp"));
    #endif
    wxPoint CurrentMousePosn = ::wxGetMousePosition();
    wxPoint fullScreen = pWin->ClientToScreen(wxPoint(mouseX, mouseY));
    MSW_MouseMove(fullScreen.x, fullScreen.y);
    INPUT Input         = {0};
    Input.type          = INPUT_MOUSE;
    Input.mi.dwFlags    = MOUSEEVENTF_LEFTUP;
    ::SendInput(1, &Input, sizeof(INPUT));
    MSW_MouseMove(CurrentMousePosn.x, CurrentMousePosn.y);
  }
  #endif
}

void CodeSnippetsTreeCtrl::MSW_MouseMove(int x, int y) {
  #if defined(__WXMSW__)
  double fScreenWidth   = ::GetSystemMetrics(SM_CXSCREEN) - 1;
  double fScreenHeight  = ::GetSystemMetrics(SM_CYSCREEN) - 1;
  double fx = x * (65535.0f / fScreenWidth);
  double fy = y * (65535.0f / fScreenHeight);
  INPUT  Input = {0};
  Input.type      = INPUT_MOUSE;
  Input.mi.dwFlags  = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
  Input.mi.dx = (LONG)fx;
  Input.mi.dy = (LONG)fy;
  ::SendInput(1, &Input, sizeof(INPUT));
  #endif
}
